package com.adhere.common.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.Map;


/**
 * jwt 工具类
 *
 * JWT 的组成部分
 * JSON Web Tokens 由三部分组成，用英文句点分割(.) ，一般看起来例如：xxxxx.yyyyy.zzzzz
 *  Header  头信息  {"alg": "HS256", "typ": "JWT"} 使用 base 64 进行加密
 *  Payload  荷载信息,实际数据 {"sub": "1234567890", "name": "John Doe","admin": true} 使用 base64 进行加密
 *  Signature  由头信息+荷载信息+密钥 组合之后进行加密得到  使用header中指定的算法将编码后的header、编码后的payload、一个secret进行加密
 *      例如 使用的是HMAC SHA256算法，大致流程类似于: HMAC-SHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
 *
 * @author WT
 * @date 2022/01/19 17:09:12
 */
public class JwtUtils {

    private static final String TOKEN_ERROR_MESSAGE = "token 不合法";
    private static final String TOKEN_EXPIRATION_MESSAGE = "token 已经过期";

    /**
     * 根据用户信息生成token
     *
     * @param claims 在 token 存放的一些自定义的信息
     * @return token
     */
    public static String generateToken (Map<String, Object> claims, Long expiration, String secret) {

        return Jwts.builder()
                .setHeaderParam("type", "JWT")
                // 设置负载信息 不设置 payload 的时候，他就是 bytes = this.payload != null ? payload.getBytes(Strings.UTF_8) : toJson(claims);
                // 因为 payload 是 json 格式的所以这样设置更加方便一点
                .setClaims(claims)
                .setExpiration(longToDate(expiration))
                // 设置加密规则 和 加解密的秘钥
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 生成token的失效时间  当前时间加上设置的失效时间(s * 1000)
     *
     * @param seconds 失效时间，单位为秒
     * @return 失效时间对应的 Date 类型
     */
    private static Date longToDate (Long seconds) {
        return new Date(System.currentTimeMillis() + seconds * 1000);
    }

    /**
     * 校验 token 的 secret 是否正确, 是否被篡改过，是否是合法的 token。
     *
     * @param token token
     * @return true 是 合法的 token
     */
    public static boolean validateToken(String token, String secret) {
        try {
            Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 判断 token 是否过期
     *
     * @param token token
     * @return true 是 过期
     */
    private static boolean expiration (String token, String secret) {
        return !validateToken(token, secret);
    }

    /**
     * 从 token 中获取 荷载信息
     *
     * @param token token
     * @return 荷载信息 payload, 当 token 不合法的时候，返回 claims 为 null
     */
    public static Claims getClaimFromToken(String token, String secret) {

        if (validateToken(token, secret)) {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        }
        return null;

    }

    /**
     * 刷新 token
     * 就是将创建时间给改一下，重新生成，重新生成的时候过期时间就会相应的改变
     *
     * @param token 原始 token
     * @return 时间刷新过后的 token, 返回 null 说明原始 token 不合法
     */
    public static String refreshToken (String token, String secret, Long expiration) {

        Claims claims = getClaimFromToken(token, secret);

        if (claims == null) {
            return null;
        }

        return Jwts.builder()
                // 设置负载信息
                .setClaims(claims)
                .setExpiration(longToDate(expiration))
                // 设置加密规则 和 加解密的秘钥
                .signWith(SignatureAlgorithm.HS512,secret)
                .compact();
    }


}
